package com.ctyun.xstore.vod.internal;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;

/**
 * 
 * @author Keijack
 *
 */
public class HttpRequestQuery {

	private final Map<String, String[]> parameterMap = new LinkedHashMap<>();

	public HttpRequestQuery() {
	}

	public HttpRequestQuery(String queryString) {
		if (this.isBlank(queryString)) {
			return;
		}
		String qstr;
		if (queryString.startsWith("?")) {
			qstr = queryString.substring(1);
		} else {
			qstr = queryString;
		}
		initParamMap(qstr);
	}

	public HttpRequestQuery(Map<String, String[]> parameterMap) {
		this.parameterMap.putAll(parameterMap);
	}

	private void initParamMap(String queryString) {
		this.parameterMap.clear();

		String[] keyAndVals = queryString.split("&");
		for (String keyAndVal : keyAndVals) {
			if (!this.isBlank(keyAndVal)) {
				String key = getKey(keyAndVal);
				String val = getVal(keyAndVal);

				addKeyValToParameterMap(key, val);
			}
		}
	}

	private void addKeyValToParameterMap(String key, String value) {
		if (!this.parameterMap.containsKey(key)) {
			String[] values = { value };
			this.parameterMap.put(key, values);
		} else {
			String[] values = this.parameterMap.get(key);
			this.parameterMap.put(key, (String[]) addToArray(values, value));
		}
	}

	private String getVal(String keyAndVal) {
		int index = keyAndVal.indexOf('=');
		String val;
		if (index < 0) {
			val = "";
		} else {
			val = keyAndVal.substring(index + 1);
		}
		return val;
	}

	private String getKey(String keyAndVal) {
		int index = keyAndVal.indexOf('=');
		String key;
		if (index < 0) {
			key = keyAndVal;
		} else {
			key = keyAndVal.substring(0, index);
		}
		return key;
	}

	public String getQueryString() {
		return combineQueryString(this.parameterMap);
	}

	public String getParameter(String key) {
		String[] paramVals = getParameterValues(key);
		if ((paramVals == null) || (paramVals.length == 0)) {
			return null;
		}
		return paramVals[0];
	}

	public String[] getParameterValues(String key) {
		return this.parameterMap.get(key);
	}

	public Map<String, String[]> getParameterMap() {
		return this.parameterMap;
	}

	public Map<String, String[]> getSortedParameterMap() {
		String[] paramNames = this.parameterMap.keySet().toArray(
				new String[this.parameterMap.keySet().size()]);
		Arrays.sort(paramNames);

		Map<String, String[]> sortedParamMap = new LinkedHashMap<>();
		for (String paramName : paramNames) {
			String[] values = this.parameterMap.get(paramName).clone();
			Arrays.sort(values);
			sortedParamMap.put(paramName, values);
		}
		return sortedParamMap;
	}

	public Map<String, String> getSingleValueParameterMap() {
		Map<String, String> singleValueMap = new LinkedHashMap<>();
		for (String key : this.parameterMap.keySet()) {
			singleValueMap.put(key, getParameter(key));
		}
		return singleValueMap;
	}

	public String getSortedQueryString() {
		return combineQueryString(getSortedParameterMap());
	}

	public Enumeration<String> getParameterNames() {
		return new Vector<String>(this.parameterMap.keySet()).elements();
	}

	public void addParameter(String key, String value) {
		addKeyValToParameterMap(key, value);
	}

	public void removeParameter(String key) {
		if (!this.parameterMap.containsKey(key)) {
			return;
		}
		this.parameterMap.remove(key);
	}

	private String combineQueryString(Map<String, String[]> parameterMap) {
		String queryString = "";
		for (Entry<String, String[]> entry : parameterMap.entrySet()) {
			for (String val : entry.getValue()) {
				if (!"".equals(queryString)) {
					queryString = queryString + "&";
				}
				queryString = queryString + entry.getKey() + "=" + val;
			}
		}
		return queryString;
	}

	public boolean contains(HttpRequestQuery queryString) {
		for (String key : queryString.getParameterMap().keySet()) {
			if (!this.parameterMap.containsKey(key)) {
				return false;
			}
			List<String> valuesInThisObject = Arrays.asList(this.parameterMap.get(key));
			List<String> valuesInOtherObject = Arrays.asList(queryString.getParameterMap().get(key));
			if (!valuesInThisObject.containsAll(valuesInOtherObject)) {
				return false;
			}
		}
		return true;
	}

	@Override
	public String toString() {
		return getQueryString();
	}

	private boolean isBlank(final CharSequence cs) {
		int strLen;
		if (cs == null || (strLen = cs.length()) == 0) {
			return true;
		}
		for (int i = 0; i < strLen; i++) {
			if (!Character.isWhitespace(cs.charAt(i))) {
				return false;
			}
		}
		return true;
	}

	private <T> T[] addToArray(final T[] array, final T element) {
		Class<?> type;
		if (array != null) {
			type = array.getClass().getComponentType();
		} else if (element != null) {
			type = element.getClass();
		} else {
			throw new IllegalArgumentException("Arguments cannot both be null");
		}
		@SuppressWarnings("unchecked") // type must be T
		final T[] newArray = (T[]) copyArrayGrow1(array, type);
		newArray[newArray.length - 1] = element;
		return newArray;
	}

	private Object copyArrayGrow1(final Object array, final Class<?> newArrayComponentType) {
		if (array != null) {
			final int arrayLength = Array.getLength(array);
			final Object newArray = Array.newInstance(array.getClass().getComponentType(), arrayLength + 1);
			System.arraycopy(array, 0, newArray, 0, arrayLength);
			return newArray;
		}
		return Array.newInstance(newArrayComponentType, 1);
	}
}
